#ifndef ML_RENDER_H
#define ML_RENDER_H

#include "AudioProcessor.h"
#include "MLObj.h"
#include "MLProgressbar.h"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

#include <atomic>
#include <memory>
#include <vector>

#include <condition_variable>
#include <future>
#include <mutex>
#include <queue>

struct FrameData {
    uint8_t* data;
    size_t count;
};

class MLRender {
public:
    MLRender(
        const std::string path,
        double frameRate,
        int frame_size_x,
        int frame_size_y,
        int totalFrames,
        int quality, 
        AudioProcessor& audio_processor
    );

    void render(std::vector<MLObj*>& objects);

private:
    static const AVCodec* _find_encoder();
    void _encode_frame(const uint8_t* src_data, AVFrame* frame);
    void encodingThreadFunc(int index);
    void writerThreadFunc();
    void writerThreadFuncWithAudio();
    void _flush_video_encoder();
    void _flush_remaining_audio(std::queue<std::pair<AVPacket, int64_t>>& packets);
    void _flush_remaining_video_packets(std::queue<std::pair<AVPacket, int64_t>>& packets, AVRational video_time_base, AVRational audio_codec_time_base, AVRational audio_stream_time_base);

	bool encodingDone = false;
    double _frame_rate;
    int _frame_size_x, _frame_size_y;
    std::string _path;
    size_t _total_frames;

    AudioProcessor _audio_processor;
    int _sample_rate = -1;

    AVCodecContext* codec_ctx;
    AVStream* stream;
    AVStream* audio_stream;
    AVFrame** frames;
    AVFormatContext* fmt_ctx;
    SwsContext* sws_ctx;
    int64_t pts = 0;
    int64_t next_video_pts = 0;

    const int encoding_threads_num = 1;
    std::mutex queue_mutex;
    std::mutex frame_mutex;
    std::condition_variable queue_cv;
    std::queue<FrameData> frame_queue;
    bool encoding_finished = false;
    std::vector<std::thread> encoding_threads;
    std::thread encoding_main_thread;
    int not_writen_frame_count = 0;
};
#endif
